#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <sys/select.h>

#include "Socket.hpp"

class SelectServer
{
public:
    SelectServer(int port)
        : _port(port), _sock(std::make_unique<sock::TcpSocket>("0.0.0.0", port)), _fd_set(sizeof(fd_set) * 8, -1)
    {
        _sock->CreateSocket();
        _sock->BindSocket();
        _sock->Listen();

        _fd_set[0] = _sock->GetSockfd();
    }

    ~SelectServer()
    {
        _sock->Close();
    }

    void AcceptClient(int i)
    {
        // listen 套接字就绪
        sockaddr_in clientAddr;
        socklen_t len = sizeof(clientAddr);
        int sockfd = _sock->Accept(&clientAddr, &len); // 这里调用accept不会阻塞，因为select已经监听到了有客户端连接
        if (sockfd < 0)
        {
            LOG(ERROR, "accept error...\n");
            return;
        }

        char ip[32];
        inet_ntop(AF_INET, &clientAddr.sin_addr, ip, sizeof(ip));

        LOG(INFO, "Get a new client, sockfd = %d, ClientInfo: %s:%d\n", sockfd, ip, ntohs(clientAddr.sin_port));

        size_t pos = 1;
        for (; pos < _fd_set.size(); ++pos)
        {
            if (_fd_set[pos] == -1)
            {
                _fd_set[pos] = sockfd;
                break;
            }
        }
        if (pos == _fd_set.size())
        {
            close(sockfd);
            LOG(WARNING, "server is full....\n");
        }

        LOG(INFO, "current fd_set: %s\n", RfdsString().c_str());
    }

    void Service(int i)
    {
        int sockfd = _fd_set[i];
        char buffer[1024];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if(n > 0)
        {
            buffer[n] = '\0';
            std::string echo_str = "[server echo]# ";
            echo_str += buffer;

            send(sockfd, echo_str.c_str(), echo_str.size(), 0);
        }
        else if(n == 0)
        {
            close(sockfd);
            _fd_set[i] = -1;
        }
        else 
        {
            LOG(ERROR, "read fail\n");
        }
    }

    void HandleEvent(fd_set & rfds)
    {
        for (size_t i = 0; i < _fd_set.size(); ++i)
        {
            if (_fd_set[i] == -1)
                continue;
            if (FD_ISSET(_fd_set[i], &rfds))
            {
                AcceptClient(i);
            }
            else
            {
                // 普通 socket 读取事件就绪
                Service(i);
            }
        }
    }

    void Loop()
    {
        while (true)
        {

            fd_set rfds; // 读文件描述符集合
            FD_ZERO(&rfds);
            // 将 _fd_set 中的文件描述符添加进 rfds 中
            int maxfd = -1;
            for (size_t i = 0; i < _fd_set.size(); ++i)
            {
                if (_fd_set[i] != -1)
                {
                    FD_SET(_fd_set[i], &rfds);
                    if (_fd_set[i] > maxfd)
                    {
                        maxfd = _fd_set[i];
                    }
                }
            }
            FD_SET(_sock->GetSockfd(), &rfds);

            // timeval timeout;
            // timeout.tv_sec = 1;
            // timeout.tv_usec = 0;
            timeval timeout = {1, 0};
            // rfds 每次调用都要进行重新设定
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, &timeout);

            switch (n)
            {
            case 0:
                LOG(INFO, "timeout...\n");
                break;
            case -1:
                LOG(INFO, "select error...\n");
                break;
            default:
                LOG(INFO, "event happen...\n");
                HandleEvent(rfds);
                break;
            }
        }
    }

    std::string RfdsString()
    {
        std::string ret;
        for(int i = 0; i < _fd_set.size(); ++i)
        {   
            if(_fd_set[i] != -1)
            {
                ret += std::to_string(_fd_set[i]);
                ret += " ";       
            }
        }

        return ret;
    }

private:
    std::unique_ptr<sock::TcpSocket> _sock; // 封装的 Tcp套接字的接口
    uint16_t _port;                         // 端口
    std::vector<int> _fd_set;               // 辅助数组
};